import { useGLTF, useTexture } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';

const clamp = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max);

const Whale = () => {
  const model = useGLTF('/models/whale.glb');
  const sprite = useTexture('/textures/disc.png');
  const groupRef = useRef<THREE.Group>(null!);
  const [nodes, setNodes] = useState<React.ReactNode[]>([]);

  useEffect(() => {
    sprite.colorSpace = THREE.SRGBColorSpace;
    const nodes: React.ReactNode[] = [];
    model.scene.traverse(child => {
      if (child instanceof THREE.Mesh) {
        const geometry = child.geometry;
        const material = new THREE.PointsMaterial({
          // color: 0x0378ff,
          size: 0.6,
          alphaTest: 0.5,
          transparent: true,
          blending: THREE.AdditiveBlending,
          map: sprite,
          vertexColors: true,
        });
        const wireframe = new THREE.WireframeGeometry(geometry);

        nodes.push(
          <lineSegments
            key={`${child.id}-line`}
            geometry={wireframe}
            material-depthTest={false}
            material-opacity={0.006}
            material-transparent={true}
          />,
        );
        nodes.push(<points key={`${child.id}-point`} geometry={geometry} material={material} />);
      }
    });
    setNodes(nodes);
  }, [model, sprite]);

  useFrame((state, delta) => {
    const elapsedTime = state.clock.elapsedTime;
    groupRef.current.children.forEach(item => {
      if (item instanceof THREE.Points || item instanceof THREE.LineSegments) {
        const colors = [];
        const geometry = item.geometry as THREE.BufferGeometry;
        const positions = geometry.getAttribute('position');
        if (positions) {
          for (let index = 0; index < positions.count; index++) {
            const x = positions.getX(index);
            const y = positions.getY(index);
            const z = positions.getZ(index);

            const waveY = 0.03 * Math.cos(0.1 * (x / 2) + z / 12 + elapsedTime);
            positions.setY(index, y + waveY);
            const color = new THREE.Color(0x0378ff);
            color.setHSL(0.65 * clamp(Math.sin(0.1 * z + elapsedTime), 0.6, 1), 1, 0.4);
            colors.push(color.r, color.g, color.b);
          }
          geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
          geometry.attributes.position.needsUpdate = true;
          geometry.attributes.color.needsUpdate = true;
        }
      }
    });
    groupRef.current.rotation.z += delta * 0.1;
  });

  return (
    <>
      <group ref={groupRef}>{nodes}</group>
    </>
  );
};

export default Whale;
